package com.ywicc.commlibs.socket;

import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.LifecycleService;
import androidx.lifecycle.OnLifecycleEvent;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.RunnableScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * author : zhouxuan77
 * e-mail : zhouxuan77@163.com
 * time   : 2022/03/16
 * desc   : 普通 Socket 服务端
 * version: 1.0
 */
public class ServerService extends LifecycleService {

    private final static int SERVER_INTERVAL_COMM_TIME = 5;
    private final static int SERVER_INTERVAL_DATA_TIME = 2;
    private final static String TAG = "ServerService";

    public ExecutorService cachedThreadPool;
    public ScheduledThreadPoolExecutor scheduledThreadPool;
    public ServerSocket serverSocket;
    public Socket socket;

    private RunnableScheduledFuture<?> runnableWaitCOMM;
    private RunnableScheduledFuture<?> runnableReceiveData;
    private boolean isStrict;
    private byte[] sendData;
    private SocketUtils socketUtils;

    public ServerService() {
        ServerObserver observer = new ServerObserver();
        getLifecycle().addObserver(observer);
    }

    public void startReceive() {
        Log.w(TAG, "Start receive");

        runnableWaitCOMM = (RunnableScheduledFuture<?>) scheduledThreadPool.scheduleAtFixedRate(receiveWaitCOMMRunnable, 0, SERVER_INTERVAL_COMM_TIME, TimeUnit.SECONDS);
    }

    public void stopReceive() {
        Log.w(TAG, "Stop receive");

        if (runnableWaitCOMM != null) {
            scheduledThreadPool.remove(runnableWaitCOMM);
        }

        if (runnableReceiveData != null) {
            scheduledThreadPool.remove(runnableReceiveData);
        }
    }

    public void setStrictModule(int module) {
        isStrict = module == SocketUtils.SERVER_RECEIVE_STRICT;
    }

    public void sendData(byte[] data) {
        Log.w(TAG, "Start send data");

        sendData = data;
        cachedThreadPool.execute(sendDataRunnable);
    }

    Runnable receiveWaitCOMMRunnable = new Runnable() {
        @Override
        public void run() {
            try {
                Log.w(TAG, "Wait for connect");

                socket = serverSocket.accept();

                // 确保同一时间，有且仅有一个 Socket 连接
                if (runnableReceiveData != null) {
                    scheduledThreadPool.remove(runnableReceiveData);
                }

                runnableReceiveData = (RunnableScheduledFuture<?>) scheduledThreadPool.scheduleAtFixedRate(receiveDataRunnable, 0, SERVER_INTERVAL_DATA_TIME, TimeUnit.SECONDS);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    };

    Runnable receiveDataRunnable = new Runnable() {
        @Override
        public void run() {
            Log.w(TAG, "Wait for receive data");

            byte[] receivedData = socketUtils.receiveData(socket, isStrict);

            if (receivedData != null && serviceImpl != null) {
                serviceImpl.callBackServerReceiveData(receivedData);
            }
        }
    };

    Runnable sendDataRunnable = new Runnable() {
        @Override
        public void run() {
            socketUtils.sendData(socket, sendData, isStrict);
        }
    };

    @Nullable
    @Override
    public IBinder onBind(@NonNull Intent intent) {
        super.onBind(intent);
        Log.w(TAG, "Service bind succeed");

        startReceive();
        return new ServerBinder();
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.w(TAG, "Service unbind succeed");

        stopReceive();
        return super.onUnbind(intent);
    }

    public class ServerBinder extends Binder {
        public ServerService getService() {
            return ServerService.this;
        }
    }

    ServerServiceImpl serviceImpl;

    public interface ServerServiceImpl {
        void callBackServerReceiveData(byte[] data);
    }

    public void setServerServiceImpl(ServerServiceImpl serviceImpl) {
        this.serviceImpl = serviceImpl;
    }

    class ServerObserver implements LifecycleObserver {

        @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
        public void initSocket() {
            Log.w(TAG, "Service start succeed");

            cachedThreadPool = Executors.newCachedThreadPool();
            scheduledThreadPool = new ScheduledThreadPoolExecutor(5);

            cachedThreadPool.execute(initSocketRunnable);

            socketUtils = new SocketUtils();
        }

        @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
        public void stopSocket() {
            Log.w(TAG, "Service destroy succeed");

            cachedThreadPool.shutdown();
            scheduledThreadPool.shutdown();

            try {
                if (serverSocket != null) {
                    serverSocket.close();
                }

                if (socket != null) {
                    socket.shutdownOutput();
                    socket.shutdownInput();

                    socket.close();
                }

                Log.w(TAG, "Socket stop succeed");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        Runnable initSocketRunnable = new Runnable() {
            @Override
            public void run() {
                try {
                    Log.w(TAG, "Server socket start");

                    serverSocket = new ServerSocket(SocketUtils.IPPort);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        };
    }
}
